package org.cybzacg.convert.converters;

import org.cybzacg.convert.annotation.Converter;
import org.cybzacg.convert.context.ConversionContext;
import org.cybzacg.convert.core.AbstractTypeConverter;
import org.cybzacg.convert.enums.ConversionError;
import org.cybzacg.convert.enums.ConversionPriority;
import org.cybzacg.convert.enums.ConversionType;
import org.cybzacg.convert.exception.ConversionException;
import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 枚举类型转换器
 * 支持各种枚举类型之间的转换
 *
 * @author cybzacg.blog
 * @version 3.0
 * @since 3.0
 */
@Slf4j
@Converter(
    name = "EnumConverter",
    priority = ConversionPriority.LOW,
    description = "枚举类型转换器，支持各种类型到枚举的转换",
    enabled = true,
    version = "3.0",
    author = "cybzacg.blog",
    supportedTypes = {Enum.class},
    tags = {"enum", "custom", "conversion"}
)
public class EnumConverter extends AbstractTypeConverter {

    // 枚举常量缓存
    private static final Map<Class<?>, Map<String, Object>> ENUM_CACHE = new ConcurrentHashMap<>();

    public EnumConverter() {
        super(ConversionPriority.LOW);
        log.debug("EnumConverter 初始化完成，优先级: {}", ConversionPriority.LOW);
    }

    @Override
    protected ConversionType getConversionType() {
        return ConversionType.ENUM;
    }

    @Override
    @SuppressWarnings("unchecked")
    protected <T> T doConvert(Object source, Class<T> targetType, T defaultValue, ConversionContext context)
            throws Exception {

        log.debug("开始枚举类型转换: 源类型={}, 目标类型={}, 默认值={}",
                 source != null ? source.getClass().getSimpleName() : "null",
                 targetType.getSimpleName(),
                 defaultValue);

        if (source == null) {
            log.debug("源对象为null，返回默认值: {}", defaultValue);
            return defaultValue;
        }

        // 如果已经是目标类型，直接返回
        if (targetType.isInstance(source)) {
            log.debug("源对象已经是目标类型，直接返回: {}", source);
            return (T) source;
        }

        // 检查目标类型是否为枚举
        if (!targetType.isEnum()) {
            throw new ConversionException(ConversionError.UNSUPPORTED_CONVERSION,
                "目标类型不是枚举类型: " + targetType.getSimpleName());
        }

        // 转换为枚举
        T result = convertToEnum(source, targetType, context);
        log.debug("枚举类型转换完成: {} -> {}", source.getClass().getSimpleName(), result);

        return result;
    }

    /**
     * 将对象转换为目标枚举类型
     *
     * @param source 源对象
     * @param targetType 目标枚举类型
     * @param context 转换上下文
     * @return 枚举对象
     * @throws ConversionException 转换失败时抛出
     */
    @SuppressWarnings("unchecked")
    protected <T> T convertToEnum(Object source, Class<T> targetType, ConversionContext context)
            throws ConversionException {

        log.debug("开始将对象转换为枚举: 源类型={}, 目标枚举类型={}",
                 source != null ? source.getClass().getSimpleName() : "null",
                 targetType.getSimpleName());

        if (source == null) {
            return null;
        }

        // 字符串转枚举
        if (source instanceof String string) {
            return convertFromString(string, targetType, context);
        }

        // 数字转枚举
        if (source instanceof Number number) {
            return convertFromNumber(number, targetType, context);
        }

        // 枚举转枚举
        if (source instanceof Enum) {
            return convertFromEnum((Enum<?>) source, targetType, context);
        }

        // 其他类型转枚举
        return convertFromObject(source, targetType, context);
    }

    /**
     * 字符串转枚举
     */
    @SuppressWarnings("unchecked")
    protected <T> T convertFromString(String source, Class<T> targetType, ConversionContext context)
            throws ConversionException {

        if (source == null || source.trim().isEmpty()) {
            return null;
        }

        String enumName = source.trim();

        // 获取枚举缓存
        Map<String, Object> enumCache = getEnumCache(targetType);

        // 精确匹配
        Object enumValue = enumCache.get(enumName);
        if (enumValue != null) {
            return (T) enumValue;
        }

        // 忽略大小写匹配
        for (Map.Entry<String, Object> entry : enumCache.entrySet()) {
            if (entry.getKey().equalsIgnoreCase(enumName)) {
                return (T) entry.getValue();
            }
        }

        // 下划线转驼峰匹配
        String camelCaseName = toCamelCase(enumName);
        for (Map.Entry<String, Object> entry : enumCache.entrySet()) {
            if (entry.getKey().equalsIgnoreCase(camelCaseName)) {
                return (T) entry.getValue();
            }
        }

        // 驼峰转下划线匹配
        String underscoreName = toUnderscoreCase(enumName);
        for (Map.Entry<String, Object> entry : enumCache.entrySet()) {
            if (entry.getKey().equalsIgnoreCase(underscoreName)) {
                return (T) entry.getValue();
            }
        }

        // 检查是否启用宽松模式
        boolean lenientMode = context.getProperty("enumLenientMode") != null &&
                             Boolean.TRUE.equals(context.getProperty("enumLenientMode"));

        if (lenientMode) {
            // 宽松模式：返回第一个枚举值
            Object[] enumConstants = targetType.getEnumConstants();
            if (enumConstants != null && enumConstants.length > 0) {
                log.warn("宽松模式：无法匹配枚举值 '{}'，返回第一个枚举值: {}",
                        enumName, enumConstants[0]);
                return (T) enumConstants[0];
            }
        }

        throw new ConversionException(ConversionError.UNSUPPORTED_CONVERSION,
            "无法找到枚举值: " + enumName + " 在枚举类型: " + targetType.getSimpleName());
    }

    /**
     * 数字转枚举
     */
    @SuppressWarnings("unchecked")
    protected <T> T convertFromNumber(Number source, Class<T> targetType, ConversionContext context)
            throws ConversionException {

        if (source == null) {
            return null;
        }

        int ordinal = source.intValue();
        Object[] enumConstants = targetType.getEnumConstants();

        if (enumConstants != null && ordinal >= 0 && ordinal < enumConstants.length) {
            return (T) enumConstants[ordinal];
        }

        throw new ConversionException(ConversionError.NUMBER_OVERFLOW,
            "枚举索引超出范围: " + ordinal + " 在枚举类型: " + targetType.getSimpleName());
    }

    /**
     * 枚举转枚举
     */
    @SuppressWarnings("unchecked")
    protected <T> T convertFromEnum(Enum<?> source, Class<T> targetType, ConversionContext context)
            throws ConversionException {

        if (source == null) {
            return null;
        }

        // 根据枚举名称匹配
        return convertFromString(source.name(), targetType, context);
    }

    /**
     * 其他类型转枚举
     */
    @SuppressWarnings("unchecked")
    protected <T> T convertFromObject(Object source, Class<T> targetType, ConversionContext context)
            throws ConversionException {

        if (source == null) {
            return null;
        }

        // 尝试转换为字符串再转换
        return convertFromString(source.toString(), targetType, context);
    }

    /**
     * 获取枚举缓存
     */
    @SuppressWarnings("unchecked")
    protected static <T> Map<String, Object> getEnumCache(Class<T> targetType) {
        return ENUM_CACHE.computeIfAbsent(targetType, clazz -> {
            Map<String, Object> cache = new HashMap<>();
            Object[] enumConstants = clazz.getEnumConstants();
            if (enumConstants != null) {
                for (Object enumConstant : enumConstants) {
                    Enum<?> enumValue = (Enum<?>) enumConstant;
                    cache.put(enumValue.name(), enumConstant);
                }
            }
            return cache;
        });
    }

    /**
     * 转换为驼峰命名
     */
    protected String toCamelCase(String input) {
        if (input == null || input.isEmpty()) {
            return input;
        }

        StringBuilder result = new StringBuilder();
        boolean capitalizeNext = false;

        for (int i = 0; i < input.length(); i++) {
            char c = input.charAt(i);
            if (c == '_') {
                capitalizeNext = true;
            } else if (capitalizeNext) {
                result.append(Character.toUpperCase(c));
                capitalizeNext = false;
            } else {
                result.append(Character.toLowerCase(c));
            }
        }

        return result.toString();
    }

    /**
     * 转换为下划线命名
     */
    protected String toUnderscoreCase(String input) {
        if (input == null || input.isEmpty()) {
            return input;
        }

        StringBuilder result = new StringBuilder();

        for (int i = 0; i < input.length(); i++) {
            char c = input.charAt(i);
            if (Character.isUpperCase(c)) {
                if (i > 0) {
                    result.append('_');
                }
                result.append(Character.toLowerCase(c));
            } else {
                result.append(c);
            }
        }

        return result.toString();
    }

    @Override
    public boolean supports(Class<?> targetType) {
        return targetType != null && targetType.isEnum();
    }

    @Override
    public Class<?> getSupportedType() {
        return Enum.class;
    }

    /**
     * 检查是否为枚举类型
     *
     * @param clazz 类型
     * @return 是否为枚举类型
     */
    public static boolean isEnumType(Class<?> clazz) {
        return clazz != null && clazz.isEnum();
    }

    /**
     * 获取枚举的所有常量
     *
     * @param enumClass 枚举类型
     * @return 枚举常量数组
     */
    public static Object[] getEnumConstants(Class<?> enumClass) {
        return enumClass != null && enumClass.isEnum() ? enumClass.getEnumConstants() : null;
    }

    /**
     * 根据名称获取枚举值
     *
     * @param <T> 枚举类型
     * @param enumClass 枚举类型
     * @param name 枚举名称
     * @return 枚举值
     */
    @SuppressWarnings("unchecked")
    public static <T extends Enum<T>> T getEnumByName(Class<T> enumClass, String name) {
        if (enumClass == null || name == null || name.trim().isEmpty()) {
            return null;
        }

        try {
            return Enum.valueOf(enumClass, name.trim());
        } catch (IllegalArgumentException e) {
            // 尝试忽略大小写匹配
            T[] constants = enumClass.getEnumConstants();
            if (constants != null) {
                for (T constant : constants) {
                    if (constant.name().equalsIgnoreCase(name.trim())) {
                        return constant;
                    }
                }
            }
            return null;
        }
    }

    /**
     * 根据序号获取枚举值
     *
     * @param <T> 枚举类型
     * @param enumClass 枚举类型
     * @param ordinal 序号
     * @return 枚举值
     */
    @SuppressWarnings("unchecked")
    public static <T extends Enum<T>> T getEnumByOrdinal(Class<T> enumClass, int ordinal) {
        T[] constants = enumClass.getEnumConstants();
        if (constants != null && ordinal >= 0 && ordinal < constants.length) {
            return constants[ordinal];
        }
        return null;
    }

    /**
     * 安全的枚举转换
     *
     * @param <T> 目标类型
     * @param source 源对象
     * @param targetType 目标类型
     * @return 转换结果
     */
    @SuppressWarnings("unchecked")
    public static <T> T convertSafely(Object source, Class<T> targetType) {
        if (source == null) {
            return null;
        }

        if (targetType.isInstance(source)) {
            return (T) source;
        }

        if (!targetType.isEnum()) {
            throw new ConversionException(ConversionError.UNSUPPORTED_CONVERSION,
                "目标类型不是枚举类型: " + targetType.getSimpleName());
        }

        EnumConverter converter = new EnumConverter();
        try {
            return converter.convertToEnum(source, targetType, null);
        } catch (Exception e) {
            throw new ConversionException(ConversionError.CONVERSION_FAILED,
                "安全枚举转换失败: " + source + " -> " + targetType, e);
        }
    }

    /**
     * 清除枚举缓存
     */
    public static void clearCache() {
        ENUM_CACHE.clear();
        log.debug("枚举缓存已清除");
    }

    /**
     * 预加载枚举缓存
     *
     * @param enumClasses 枚举类型数组
     */
    public static void preloadCache(Class<?>... enumClasses) {
        if (enumClasses != null) {
            for (Class<?> enumClass : enumClasses) {
                if (enumClass != null && enumClass.isEnum()) {
                    getEnumCache(enumClass);
                    log.debug("预加载枚举缓存: {}", enumClass.getSimpleName());
                }
            }
        }
    }
}
